﻿using System;
using ParboiledSharp.parboiled.dataobjects;
using ParboiledSharp.parboiled.rules;
using System.Collections.Generic;
using System.Diagnostics;

namespace ParboiledSharp
{
    abstract class BaseParser
    {
        Dictionary<IntPtr, Rule> ruleCache = new Dictionary<IntPtr, Rule>();

        protected Rule Rule(Func<Rule> rule)
        {
            IntPtr pointer = rule.Method.MethodHandle.GetFunctionPointer();
            if (ruleCache.ContainsKey(pointer))
            {
                return ruleCache[pointer];
            }
            else
            {
                Sequence seq = new Sequence();
                ruleCache.Add(pointer, seq);
                Rule r = rule.Invoke();
                seq.SetRule(r);
                string execMethod = getExecutingMethod();
                seq.SetName(execMethod);
                return seq;
            }
        }

        protected string getExecutingMethod() { 
            StackFrame fr = new StackFrame(2,true);
            StackTrace st = new StackTrace(fr);
            return fr.GetMethod().Name;
        }

        /// <summary>
        /// Custom user rule. Useful for creation semantic action
        /// </summary>
        static protected Rule ACTION(Func<Context, bool> action)
        {
            return new ParboiledSharp.parboiled.rules.Action(action);
        }

        /// <summary>
        /// Custom user rule. Useful for creation semantic action
        /// </summary>
        static protected Rule ACTION(System.Action<Context> action)
        {
            return new ParboiledSharp.parboiled.rules.Action(action);
        }

        /// <summary>
        /// Match for any character(only one character)
        /// </summary>
        static protected Rule ANY()
        {
            return new Any();
        }

        /// <summary>
        /// Match if given input character is in [c1,c2] range
        /// </summary>
        static protected Rule CHARRANGE(char c1, char c2)
        {
            return new CharRange(c1, c2);
        }

        /// <summary>
        /// Go throught the rules until one of the parameter rule returns TRUE
        /// </summary>
        static protected Rule FIRST(params object[] rules)
        {
            return new FirstOf(rules);
        }

        /// <summary>
        /// Returns TRUE only if all parameter rules return FALSE
        /// </summary>
        static protected Rule NONE(params object[] rules)
        {
            return new None(rules);
        }

        /// <summary>
        /// Returns TRUE only if all parameter rules return TRUE
        /// </summary>
        static protected Rule SEQUENCE(params object[] rules)
        {
            return new Sequence(rules);
        }

        /// <summary>
        /// Returns TRUE only if all parameter rules return TRUE.
        /// Also in case of successful executing of all rules, makes "push" of all matched string
        /// </summary>
        static protected Rule SEQUENCEPUSH(params object[] rules)
        {
            return new SequencePush(rules);
        }

        /// <summary>
        /// Repeats execution of the parameter rule, until it returns FALSE.
        /// If at least one itertion returns TRUE, ONEORMORE rule returns TRUE
        /// </summary>
        static protected Rule ONEORMORE(object rules)
        {
            return new OneOrMore(rules);
        }

        /// <summary>
        /// Repeats execution of the parameter rule, until it returns FALSE.
        /// Always return TRUE
        /// </summary>
        static protected Rule ZEROORMORE(object rules)
        {
            return new ZeroOrMore(rules);
        }

        /// <summary>
        /// Tries to execute parameter rule
        /// Always return TRUE
        /// </summary>
        static protected Rule OPTIONAL(object rules)
        {
            return new Optional(rules);
        }

        /// <summary>
        /// Tries to execute parameter rule, returns the result
        /// Restore input and value stack in previous position
        /// </summary>
        static protected Rule TEST(object rules)
        {
            return new Test(rules);
        }

        /// <summary>
        /// Tries to execute parameter rule, returns the inverted result
        /// Restore input and value stack in previous position
        /// </summary>
        static protected Rule TESTNOT(object rules)
        {
            return new TestNot(rules);
        }

        /// <summary>
        /// Push last matched string to the top of the value stack
        /// </summary>
        // ReSharper disable InconsistentNaming
        static protected Rule PUSH()
        // ReSharper restore InconsistentNaming
        {
            return new PushMatch();
        }

        /// <summary>
        /// Push last matched string and add prefix to it and push it to the top of the value stack
        /// </summary>
        // ReSharper disable InconsistentNaming
        static protected Rule PUSH(string prefix)
        // ReSharper restore InconsistentNaming
        {
            return new PushMatch(prefix);
        }

        /// <summary>
        /// Push any string to the top of the value stack
        /// </summary>
        // ReSharper disable InconsistentNaming
        static protected Rule PUSHSTRING(string str)
        // ReSharper restore InconsistentNaming
        {
            return new PushString(str);
        }

        /// <summary>
        /// Push last matched string to the top of the value stack
        /// </summary>
        // ReSharper disable InconsistentNaming
        static protected Rule EOI()
        // ReSharper restore InconsistentNaming
        {
            return new Eoi();
        }

        /// <summary>
        /// Tries to match parameter string and the input. Case sensitive
        /// </summary>
        static protected Rule STRING(string match)
        {
            return new StringRule(match, false);
        }

        /// <summary>
        /// Tries to match parameter string and the input. Case sensitive
        /// </summary>
        static protected Rule STRING(char match)
        {
            return new StringRule(new string(match,1), false);
        }

        /// <summary>
        /// Tries to match parameter string and the input. Case insensitive
        /// </summary>
        static protected Rule STRING_IGNORE_CASE(string match)
        {
            return new StringRule(match, true);
        }

        /// <summary>
        /// Print the message to the stdout.
        /// </summary>
        static protected Rule PRINT(string message)
        {
            return new DebugPrintAction(message, false);
        }

        /// <summary>
        /// Print the message with new line to the stdout.
        /// </summary>
        static protected Rule PRINTLN(string message)
        {
            return new DebugPrintAction(message, true);
        }

        /// <summary>
        /// Print match
        /// </summary>
        static protected Rule PRINTMATCH()
        {
            return ACTION(delegate(Context c)
            {
                Console.WriteLine(c.GetMatch());
            });
        }
    }
}